﻿using Microsoft.Practices.Unity;
using Sasa.QualityTools.CodeAnalysis.Common;
using Sasa.QualityTools.CodeAnalysis.Core.Utilities;
using Sasa.QualityTools.CodeAnalysis.Core.VisualStudio;
using Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.ViewModels;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.Commands.Behaviors.CodeAnalysis.Inspection
{
    public class ShowCodeInspectionResultBehavior : ICommandBehavior<CodeAnalysisCommandExecutionContext>
    {
        private IUnityContainer container;


        public ShowCodeInspectionResultBehavior(IUnityContainer container)
        {
            this.container = container;
        }


        public void Behave(CodeAnalysisCommandExecutionContext context)
        {
            if (context.Progress.Status != CodeAnalysisStatus.Completed)
            {
                return;
            }

            var ide = container.Resolve<IIde>();
            ide.FindRuleDetailView().DataContext = BuildUpRuleDetail(context.Progress.CodeAnalyzers.OfType<CodeInspectorViewModel>());

            var model = BuildUpCodeInspectionResult(context.Progress.CodeAnalyzers.OfType<CodeInspectorViewModel>());
            var view = ide.FindCodeInspectionResultView();
            view.DataContext = model;
            view.Show();

            model.GoToFirstPage();
            model.ClearHistories();
        }

        private RuleDetailViewModel BuildUpRuleDetail(IEnumerable<CodeInspectorViewModel> analyses)
        {
            var model = new RuleDetailViewModel();

            foreach (var analysis in analyses)
            {
                foreach (var rule in analysis.Rules)
                {
                    model.Rules[rule.CheckId] = rule;
                }
            }

            var ide = container.Resolve<IIde>();
            model.ShowMoreDetailRequired += delegate
            {
                if (model.Current.HasUrl)
                {
                    ide.Navigate(model.Current.Url);
                }
                else
                {
                    ide.NotifyWarn("FxCop Integrator can't open the rule detail page of this rule because the url of the detail page ('{0}') is invalid.", model.Current.Url);
                }
            };
            model.BackRequired += delegate
            {
                ide.FindCodeInspectionResultView().Show();
            };

            return model;
        }

        private CodeInspectionResultViewModel BuildUpCodeInspectionResult(IEnumerable<CodeInspectorViewModel> inspectors)
        {
            var model = new CodeInspectionResultViewModel();

            var ide = container.Resolve<IIde>();
            var mapping = ide.Solution.GetBuildTargetProjects(false)
                .Where(o => o.IsSupportedCodeAnalysis)
                .ToDictionary(o => o.AssemblyPath);

            foreach (var inspector in inspectors)
            {
                foreach (var problem in inspector.Problems)
                {
                    if (problem.AssemblyPath != null && mapping.ContainsKey(problem.AssemblyPath))
                    {
                        var prj = mapping[problem.AssemblyPath];
                        problem.ProjectName = prj.Name;
                        if (problem.Path == null && problem.TypeName != null)
                        {
                            problem.Path = prj.GetFilePathFromTypeName(problem.TypeName);
                        }
                    }
                    model.Rows.Add(problem);
                }
            }

            model.CodeFocusRequested += (sender, e) =>
            {
                if (e.Path != null)
                {
                    ide.Solution.Open(e.Path, e.Line);
                }
            };
            model.ShowRuleRequired += (sender, e) =>
            {
                var view = ide.FindRuleDetailView();
                var vm = (RuleDetailViewModel)view.DataContext;
                vm.Current = vm.Rules[e.Problem.CheckId];
                vm.ProblemMessage = e.Problem.Message;
                vm.SuppressMessage = Text.Format("[SuppressMessage(\"{0}\", \"{1}:{2}\")]", vm.Current.Category, vm.Current.CheckId, vm.Current.TypeName);
                view.Show();
            };
            model.ExportReportRequired += (sender, e) =>
            {
                using (var dialog = new SaveFileDialog())
                {
                    dialog.Title = "Export Code Inspection Result Report";
                    dialog.DefaultExt = "csv";
                    dialog.Filter = "CSV File (*.csv)|*csv";
                    if (dialog.ShowDialog() == DialogResult.OK)
                    {
                        var generator = new ReportGenerator<ProblemViewModel>();
                        generator.RegisterColumn("Project", o => o.ProjectName);
                        generator.RegisterColumn("Kind", o => o.Kind);
                        generator.RegisterColumn("Id", o => o.Id);
                        generator.RegisterColumn("CheckId", o => o.CheckId);
                        generator.RegisterColumn("Category", o => o.Category);
                        generator.RegisterColumn("Message", o => o.Message);
                        generator.RegisterColumn("Path", o => o.Path);
                        generator.RegisterColumn("Line", o => (o.Line.HasValue) ? o.Line.ToString() : "");
                        generator.Generate(dialog.FileName, model.Rows);
                        ide.NotifyInfo("Exported code inspection result.");
                    }
                }
            };
            model.GoToHowToSearchPageRequired += (sender, e) =>
            {
                ide.Navigate("http://fxcopintegrator.codeplex.com/wikipage?title=Search&referringTitle=Documentation");
            };

            return model;
        }
    }
}
